﻿using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using SimpleJSON;

public class ExportVersion
{
    static string LuaBuilderPath = string.Format("{0}/../Tools/lua/LangExtBuilder.py", Application.dataPath);
    public class UpdatePackageInfo
    {
        public string md5;
        public long size;
    }

    static string StorePath
    {
        get
        {
            return string.Format("{0}/{1}", Application.dataPath, EditorConfig.AB_STORE_PATH);
        }
    }
    static string VersionPath
    {
        get
        {
            string path = EditorConfig.VERSION_PATH;
            FileHelper.CreateDirectory(path);
            return path;
        }
    }

    static string LuaPath
    {
        get
        {
            return string.Format("{0}/{1}", Application.dataPath, Config.LUA_PATH);
        }
    }

    static string LuaVersionPath
    {
        get
        {
            string path = string.Format("{0}/lua", VersionPath);
            FileHelper.CreateDirectory(path);

            return path;
        }
    }

    struct FileInfo
    {
        public string path;
        public string md5;
    }

    // 遍历文件夹,生成文件及MD5信息
    static void GetAllFileInfo(string path, List<FileInfo> data)
    {
        string file = "";
        List<string> files = FileHelper.GetAllChildFiles(path);
        for (int i = 0; i < files.Count; ++i)
        {
            file = files[i];
            if (file.EndsWith(".meta") || file.EndsWith(".manifest") || file.Contains(".DS_Store") || file.Contains(Config.LUA_PACKAGE))
            {
                continue;
            }

            file = file.Replace("\\", "/");

            FileInfo info = new FileInfo();
            info.path = file.Replace(path, "").Substring(1);
            info.md5 = Md5Helper.GetFileMd5(file);

            data.Add(info);
        }
    }

    // 从MD5文件读取MD5列表
    static void GetFileInfoFromMd5File(string path, Dictionary<string, string> data)
    {
        string text = FileHelper.ReadTextFromFile(path);
        string[] md5s = text.Split(new[] { "\n" }, System.StringSplitOptions.RemoveEmptyEntries);

        for (int i = 0; i < md5s.Length; ++i)
        {
            string[] temp = md5s[i].Split(new[] { ":" }, System.StringSplitOptions.RemoveEmptyEntries);
            if (temp.Length == 2)
            {
                data.Add(temp[0], temp[1]);
            }
        }
    }

    // 生成文件列表MD5串
    static string GenFileListMd5String(List<FileInfo> files)
    {
        string text = "";
        for (int i = 0; i < files.Count; ++i)
        {
            FileInfo info = files[i];
            if (!string.IsNullOrEmpty(text))
            {
                text += "\n";
            }

            text += string.Format("{0}:{1}", info.path, info.md5);
        }

        return text;
    }

    static string GetCurrentVersion(string path, bool useDefault = true)
    {
        string version = "";
        string text = FileHelper.ReadTextFromFile(path);

        if (useDefault)
        {
            version = EditorHelper.FormatVersion(EditorConfig.Version);
        }

        if (!string.IsNullOrEmpty(text))
        {
            JSONNode node = JSON.Parse(text);
            if (node != null)
            {
                version = node["version"];
            }
        }

        return version;
    }

    public static void SaveVersionFile(string path, string version, Dictionary<string, UpdatePackageInfo> packages)
    {
        string[] v = version.Split('.');
        string url = string.Format("{0}{1}/{2}/{3}/", ConfigHelper.UpdateDataURL, ConfigHelper.UpdateName, EditorConfig.UpdateVersionName, v[2]);

        // 生成版本文件
        JSONClass jsonSave = new JSONClass();
        jsonSave.Add("url", new JSONData(url));
        jsonSave.Add("version", new JSONData(version));

        JSONClass jsonList = new JSONClass();
        jsonSave.Add("list", jsonList);

        var iter = packages.GetEnumerator();
        while (iter.MoveNext())
        {
            JSONClass item = new JSONClass();
            item.Add("md5", iter.Current.Value.md5);
            item.Add("size", iter.Current.Value.size.ToString());

            jsonList.Add(iter.Current.Key, item);
        }

        FileHelper.SaveTextToFile(jsonSave.ToString(""), path);
    }

    public static void RefreshExternalVersionFile(string version)
    {
        string path = string.Format("{0}/../version/version", Application.dataPath);

        JSONClass node = null;
        if (File.Exists(path))
        {
            string text = FileHelper.ReadTextFromFile(path);
            node = JSON.Parse(text) as JSONClass;
            node["platform"] = new JSONData(Config.UpdatePlatformName);
            node["version"] = new JSONData(version);
            node["name"] = new JSONData(ConfigHelper.ProjectName);
            node["release"] = new JSONData((int)ConfigHelper.Release);
        }
        else
        {
            node = new JSONClass();
            node.Add("platform", new JSONData(Config.UpdatePlatformName));
            node.Add("version", new JSONData(EditorConfig.Version));
            node.Add("name", new JSONData(ConfigHelper.ProjectName));
            node.Add("release", new JSONData((int)ConfigHelper.Release));
        }

        FileHelper.SaveTextToFile(node.ToString(""), path);
    }

    // 生成首次版本信息
    public static void InitVersion()
    {
        string pathDst = VersionPath;

        FileHelper.CreateDirectory(pathDst);

        string pathVersion = string.Format("{0}/version", pathDst);
        string versionNew = EditorHelper.FormatVersion(EditorConfig.Version);

        // 资源MD5
        {
            string pathSrc = StorePath;
            List<FileInfo> files = new List<FileInfo>();
            GetAllFileInfo(pathSrc, files);
            if (files.Count == 0)
            {
                return;
            }

            string md5TextNew = GenFileListMd5String(files);

            // 写入md5
            string pathMD5New = string.Format("{0}/md5_{1}", pathDst, versionNew.Replace('.', '_'));
            FileHelper.SaveTextToFile(md5TextNew, pathMD5New);
        }

        // Lua MD5
        {
            string pathSrc = LuaPath;

            List<FileInfo> files = new List<FileInfo>();
            GetAllFileInfo(pathSrc, files);
            if (files.Count == 0)
            {
                return;
            }

            string md5TextNew = GenFileListMd5String(files);

            // 写入md5
            string pathMD5New = string.Format("{0}/md5", LuaVersionPath);
            FileHelper.SaveTextToFile(md5TextNew, pathMD5New);

            // 缓存当前的多语言
            string pathLanguage = string.Format("{0}/game/utils/languages", LuaPath);
            string tempLangPath = string.Format("{0}/languages", LuaVersionPath);

            List<string> langFiles = GetLangFileNames(pathLanguage);
            foreach (string file in langFiles)
            {
                FileHelper.CopyFileTo(string.Format("{0}/{1}", pathLanguage, file), string.Format("{0}/{1}", tempLangPath, file));
            }
        }

        // 写入新版本号
        Dictionary<string, UpdatePackageInfo> packages = new Dictionary<string, UpdatePackageInfo>();
        SaveVersionFile(pathVersion, versionNew, packages);
        RefreshExternalVersionFile(versionNew);
        Debug.Log("Version Make Completed, version: " + versionNew);
    }

    static List<string> GetLangFileNames(string path)
    {
        List<string> files = new List<string>();
        if (!Directory.Exists(path))
        {
            return files;
        }

        string[] temps = Directory.GetFiles(path);
        foreach (string file in temps)
        {
            if (!file.ToLower().EndsWith(".lua"))
            {
                continue;
            }

            string filename = file.Substring(file.LastIndexOf("/") + 1);
            if (!filename.ToLower().StartsWith("local_"))
            {
                continue;
            }

            files.Add(filename);
        }

        return files;
    }

    delegate bool FileFilter(FileInfo info);
    static List<string> GetDifferentFile(List<FileInfo> current, string pathMD5, FileFilter filter = null)
    {
        Dictionary<string, string> old = new Dictionary<string, string>();
        GetFileInfoFromMd5File(pathMD5, old);

        List<string> newFiles = new List<string>();
        for (int i = 0; i < current.Count; ++i)
        {
            FileInfo info = current[i];

            string md5;
            if (!info.path.Contains(Config.LUA_PATCH) && old.TryGetValue(info.path, out md5))
            {
                if (string.Equals(md5, info.md5))
                {
                    continue;
                }
            }

            if (filter != null && filter(info))
            {
                continue;
            }

            newFiles.Add(info.path);
        }

        return newFiles;
    }

    static List<string> GetDifferentFileInStreamAssets(List<FileInfo> files, string pathMD5)
    {
        return GetDifferentFile(files, pathMD5, delegate (FileInfo info) {
            if (info.path.Contains(string.Format("{0}.{1}", Config.LUA_PACKAGE, Config.AB_SUFFIX)))
            {
                return true;
            }

            return false;
        });
    }

    static void BuildLangExt()
    {
        string pathCurrent = string.Format("{0}/game/utils/languages", LuaPath);
        string pathOrign = string.Format("{0}/languages", LuaVersionPath);
        string pathDest = string.Format("{0}/ext/", pathCurrent);

        List<string> files = GetLangFileNames(pathCurrent);
        foreach (string file in files)
        {
            string pathA = string.Format("{0}/{1}", pathOrign, file);
            string pathB = string.Format("{0}/{1}", pathCurrent, file);

            string command = "python";
            string args = string.Format("{0} {1} {2} {3}", LuaBuilderPath, pathA, pathB, pathDest);

            // 调用外部Shell编译
            ShellHelper.ProcessCommandEx(command, args);
        }
    }

    static bool BuildLuaPatch()
    {
        BuildLangExt();

        string pathSrc = LuaPath;
        string pathDst = LuaVersionPath;

        FileHelper.CreateDirectory(pathDst);

        // 收集所有的lua
        List<FileInfo> files = new List<FileInfo>();
        GetAllFileInfo(pathSrc, files);
        if (files.Count == 0)
        {
            Debug.Log("No Found Any File");
            return false;
        }

        // 收集差异的lua
        string pathMD5 = string.Format("{0}/md5", pathDst);
        List<string> newFiles = GetDifferentFile(files, pathMD5, (info) => {
            if (info.path.StartsWith("game/utils/languages/local_"))
            {
                return true;
            }
            return false;
        });
        if (newFiles.Count == 0)
        {
            Debug.Log("No More New Version");
            return false;
        }

        // 拷贝到版本文件夹
        string pathNewVersion = string.Format("{0}/luapatch", LuaVersionPath);
        FileHelper.CreateDirectory(pathNewVersion);

        for (int i = 0; i < newFiles.Count; ++i)
        {
            FileHelper.CopyFileTo(string.Format("{0}/{1}", pathSrc, newFiles[i]), string.Format("{0}/{1}", pathNewVersion, newFiles[i]));
        }

        // 编译lua
        string pathLuaPatchTemp = string.Format("{0}/{1}", Application.dataPath, EditorConfig.TEMP_LUA_PATCH_PATH);
        LuaBuilder.Build(pathNewVersion, pathLuaPatchTemp, true);

        FileHelper.DeleteDirectory(pathNewVersion);

        ImporterAssetBundle.ReimportLua(true);

        // 仅仅只是输出看一下而已,没什么用,主要是想看一下哪一些lua发生了变化
        string md5TextNew = GenFileListMd5String(files);
        string pathMD5Now = string.Format("{0}/md5_now", pathDst);
        FileHelper.SaveTextToFile(md5TextNew, pathMD5Now);
        return true;
    }

    static void RemoveLuaPatch(LuaArchType arch)
    {
        string name = FileHelper.ConvertArchName(arch, EditorConfig.TEMP_LUA_PATCH_PATH);
        string path = string.Format("Assets/{0}", name);

        FileHelper.DeleteDirectory(path);
    }

    public static void RemoveLuaPatchs()
    {
        RemoveLuaPatch(LuaArchType.BIT_32);
        RemoveLuaPatch(LuaArchType.BIT_64);

        AssetDatabase.Refresh();
    }

    // 导出一个版本
    static bool ExportOneVersion(int mirror, string versionOld, string versionNew, List<FileInfo> files, Dictionary<string, UpdatePackageInfo> packages)
    {
        string pathSrc = StorePath;
        string pathDst = VersionPath;
        string pathMD5 = string.Format("{0}/md5_{1}", pathDst, versionOld.Replace('.', '_'));

        List<string> newFiles = GetDifferentFileInStreamAssets(files, pathMD5);

        int minor_o = EditorHelper.GetVersionCode(versionOld)[2];
        int minor_n = EditorHelper.GetVersionCode(versionNew)[2];

        // 创建版本文件夹
        string versionName = string.Format("{0:D3}_{1:D3}", minor_o, minor_n);
        string pathNewVersion = string.Format("{0}/{1:D3}/{2}", pathDst, mirror, versionName);
        FileHelper.CreateDirectory(pathNewVersion);

        // 拷贝到版本文件夹
        for (int i = 0; i < newFiles.Count; ++i)
        {
            FileHelper.CopyFileTo(string.Format("{0}/{1}", pathSrc, newFiles[i]), string.Format("{0}/{1}", pathNewVersion, newFiles[i]));
        }

        // 打包
        string pathZip = string.Format("{0}.zip", pathNewVersion);
        ZipHelper.Pack(pathNewVersion, pathZip);

        // 删除文件夹
        FileHelper.DeleteDirectory(pathNewVersion);

        UpdatePackageInfo info = new UpdatePackageInfo();
        info.md5 = Md5Helper.GetFileMd5(pathZip);
        info.size = FileHelper.GetFileSize(pathZip);

        packages.Add(versionName, info);
        return true;
    }

    // 生成更新包
    public static void ExportInner()
    {
        RemoveLuaPatchs();

        bool hasLuaPatch = BuildLuaPatch();

        // 先打AssetBundle
        ExportAssetBundle.BuildInner(false, false);

        RemoveLuaPatchs();

        // 接着打更新包
        string pathSrc = StorePath;
        string pathDst = VersionPath;

        FileHelper.CreateDirectory(pathDst);

        string pathVersion = string.Format("{0}/version", pathDst);
        string versionNow = GetCurrentVersion(pathVersion);
        string versionNew = EditorHelper.NextVersion(versionNow);

        string pathMD5 = string.Format("{0}/md5_{1}", pathDst, versionNow.Replace('.', '_'));

        List<FileInfo> files = new List<FileInfo>();
        GetAllFileInfo(pathSrc, files);
        if (files.Count == 0)
        {
            Debug.Log("No more new version, current version is " + versionNow);
            return;
        }

        List<string> newFiles = GetDifferentFileInStreamAssets(files, pathMD5);
        if (newFiles.Count == 0 && !hasLuaPatch)
        {
            Debug.Log(string.Format("Not More Version.Current Version is {0}", versionNow));
            return;
        }

        Dictionary<string, UpdatePackageInfo> packages = new Dictionary<string, UpdatePackageInfo>();
        bool hasNew = false;
        int[] code = EditorHelper.GetVersionCode(versionNew);
        int minor = code[2];
        for (int i = 1; i < minor; ++i)
        {
            ExportOneVersion(minor, EditorHelper.GetVersion(code[0], code[1], i), versionNew, files, packages);
        }

        // 生成MD5文件
        string md5TextNew = GenFileListMd5String(files);
        string pathMD5New = string.Format("{0}/md5_{1}", pathDst, versionNew.Replace('.', '_'));
        FileHelper.SaveTextToFile(md5TextNew, pathMD5New);

        // 生成版本文件
        SaveVersionFile(pathVersion, versionNew, packages);
        Debug.Log("Version Make Completed, version: " + versionNew);
    }

    public static bool CheckVersionAndRename()
    {
        if (!Directory.Exists(EditorConfig.VERSION_PATH))
        {
            return false;
        }

        string pathVersion = string.Format("{0}/version", EditorConfig.VERSION_PATH);
        if (!File.Exists(pathVersion))
        {
            return false;
        }

        string version = GetCurrentVersion(pathVersion, false);
        int[] code = EditorHelper.GetVersionCode(version);
        if (code[2] == 1)
        {
            // 最后一位为0,说明没有打过更新包.继续使用这个大版本
            return false;
        }

        //FileHelper.RenameDirectory (EditorConfig.VERSION_PATH, string.Format ("{0}_{1}_{2:D2}", EditorConfig.VERSION_PATH, code[0], code[1]));
        return true;
    }

    public static void Export()
    {
        CommandHelper.InitPropertyFromCommandline();

        ExportInner();
    }
}